package org.hackystat.devcathlon.engine.event.impl;

import static org.junit.Assert.assertEquals;
import javax.xml.datatype.XMLGregorianCalendar;
import org.hackystat.devcathlon.configuration.ConfigurationManager;
import org.hackystat.devcathlon.engine.event.Event;
import org.hackystat.devcathlon.engine.event.EventScore;
import org.hackystat.devcathlon.engine.event.EventScoreManager;
import org.hackystat.devcathlon.engine.match.Match;
import org.hackystat.devcathlon.engine.team.Team;
import org.hackystat.devcathlon.engine.test.DevcathlonTestHelper;
import org.hackystat.sensorbase.resource.projects.jaxb.Project;
import org.hackystat.utilities.tstamp.Tstamp;
import org.junit.Before;
import org.junit.Test;

/**
 * Test case for BuildBeforeCommit.
 * 
 * @author John Zhou
 */
public class TestBuildBeforeCommit extends DevcathlonTestHelper {
  /** The actual build and commit interval. */
  private int buildCommitInterval = 10;
  /** The actual commit streak threshold. */
  private int streakThreshold = 10;
  /** The actual commit streak threshold. */
  private int bonusWeight = 10;

  private String teamName = "TestTeam";
  private String teamOwner = "TeamOwner";
  private String matchName = "TestMatch";
  private String member1 = "Member1";
  private String member2 = "Member2";

  /**
   * Sets up a Devcathlon Configuration for testing.
   * 
   * @throws Exception an error
   */
  @Before
  public void setUp() throws Exception {
    String eventName = BuildBeforeCommit.eventName;
    ConfigurationManager configManager = ConfigurationManager.getInstance();
    configManager.setBaseScoreWeight(eventName, 10);
    configManager.getConfiguration(eventName);
    configManager.setWakeupInterval(eventName, 60);
    configManager.addProperty(eventName, BuildBeforeCommit.buildCommitKey, String
        .valueOf(buildCommitInterval));
    configManager.addProperty(eventName, BuildBeforeCommit.upperThresholdKey, String
        .valueOf(streakThreshold));
    configManager.addProperty(eventName, BuildBeforeCommit.meetThresholdKey, String
        .valueOf(bonusWeight));
  }

  /**
   * Tests this event when there is no sensor data for this team.
   * 
   * @throws Exception If there is a bug.
   */
  @Test
  public void testNoData() throws Exception {
    // Create the team.
    Team team = makeTeam(teamName, teamOwner, member1, member2);

    // Now create the event instance.
    Event event = new BuildBeforeCommit();

    // Create a simple match containing a single team, a single project, and this event.
    Match match = addSimpleMatch(matchName, team, getProject(teamOwner, teamName), event);

    // Test the event with no sensor data.
    XMLGregorianCalendar timestamp = Tstamp.makeTimestamp();
    EventScore score = event.computeScore(match, team, timestamp);
    EventScoreManager.getInstance().updateEventScore(score);
    assertEquals("Check base 1", 0, score.getBaseScore());
    assertEquals("Check bonus 1", 0, score.getBonusScore(BuildBeforeCommit.bonusName));
    assertEquals("Check points 1", 0, event.computePoints(score));

    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }

  /**
   * Tests this event when there is no build before commit.
   * 
   * @throws Exception If there is a bug.
   */
  @Test
  public void testNoBuild() throws Exception {
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    Event event = new BuildBeforeCommit();
    Project project = getProject(teamOwner, teamName);
    Match match = addSimpleMatch(matchName, team, project, event);
    makeCommitData(team.getOwner(), project, Tstamp.makeTimestamp());

    // Compute Score
    XMLGregorianCalendar timestamp = Tstamp.makeTimestamp();
    EventScore score = event.computeScore(match, team, timestamp);
    EventScoreManager.getInstance().updateEventScore(score);
    assertEquals("Check base 2", -1, score.getBaseScore());
    assertEquals("Check bonus 2", 0, score.getBonusScore(BuildBeforeCommit.bonusName));
    assertEquals("Check points 2", -10, event.computePoints(score));

    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }

  /**
   * Tests this event when there is a commit after a build.
   * 
   * @throws Exception If there is a bug.
   */
  @Test
  public void testSingleBuildCommitSequence() throws Exception {
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    Event event = new BuildBeforeCommit();
    Project project = getProject(teamOwner, teamName);
    Match match = addSimpleMatch(matchName, team, project, event);
    XMLGregorianCalendar timestamp = Tstamp.makeTimestamp();
    makeBuildData(team.getOwner(), project, Tstamp.incrementMinutes(timestamp, -5));
    makeCommitData(team.getOwner(), project, timestamp);

    // Compute Score
    XMLGregorianCalendar evtTimestamp = Tstamp.makeTimestamp();
    EventScore score = event.computeScore(match, team, evtTimestamp);
    EventScoreManager.getInstance().updateEventScore(score);
    assertEquals("Check base 3", 1, score.getBaseScore());
    assertEquals("Check bonus 3", 0, score.getBonusScore(BuildBeforeCommit.bonusName));
    assertEquals("Check points 3", 10, event.computePoints(score));

    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }

  /**
   * Tests this event when there is a streak of build/commit sequences.
   * 
   * @throws Exception If there is a bug.
   */
  @Test
  public void testBuildCommitBonusSequence() throws Exception {
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    Event event = new BuildBeforeCommit();
    Project project = getProject(teamOwner, teamName);
    Match match = addSimpleMatch(matchName, team, project, event);
    XMLGregorianCalendar timestamp = Tstamp.makeTimestamp();
    for (int i = 0; i < 10; i++) {
      makeBuildData(team.getOwner(), project, Tstamp.incrementMinutes(timestamp, -3));
      makeCommitData(team.getOwner(), project, timestamp);
      timestamp = Tstamp.incrementMinutes(timestamp, -6);
    }

    // Compute Score
    XMLGregorianCalendar evtTimestamp = Tstamp.makeTimestamp();
    EventScore score = event.computeScore(match, team, evtTimestamp);
    EventScoreManager.getInstance().updateEventScore(score);
    assertEquals("Check base 4", 10, score.getBaseScore());
    assertEquals("Check bonus 4", 1, score.getBonusScore(BuildBeforeCommit.bonusName));
    assertEquals("Check points 4", 110, event.computePoints(score));

    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }
}
